#include "MyXmppClient.h"

#include "QXmppLogger.h"
#include "QXmppRosterManager.h"
#include "QXmppVersionManager.h"
#include "QXmppConfiguration.h"

#include <QDebug>
#include <QCryptographicHash>

//#include <QXmppReconnectionManager.h>

QString MyXmppClient::myVersion = "0.4.0";

QString MyXmppClient::getBareJidByJid( const QString &jid )
{
    QString bareJid = jid;
    if( jid.indexOf('/') >= 0 ) {
        bareJid = jid.split('/')[0];
    }
    return bareJid;
}

MyXmppClient::MyXmppClient() : QObject(0)
{
    //QXmppLogger::getLogger()->setLoggingType(QXmppLogger::StdoutLogging);

    cacheIM = new MyCache(this);
    msgWrapper = new MessageWrapper(this);
    rosterXml = new RosterXml( this );

    xmppClient = new QXmppClient( this );
    QObject::connect( xmppClient, SIGNAL(stateChanged(QXmppClient::State)), this, SLOT(clientStateChanged(QXmppClient::State)) );
    QObject::connect( xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceivedSlot(QXmppMessage)) );
    QObject::connect( xmppClient, SIGNAL(presenceReceived(QXmppPresence)), this, SLOT(presenceReceived(QXmppPresence)) );
    QObject::connect( xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(error(QXmppClient::Error)) );

    QObject::connect( msgWrapper, SIGNAL(openChat(QString)), this, SLOT(openChat(QString)) );
    //QObject::connect( msgWrapper, SIGNAL(sendMyMsg(QXmppMessage)), xmppClient, SLOT(sendPacket(QXmppPacket)) );

    xmppMessageReceiptManager = new QXmppMessageReceiptManager();
    QObject::connect( xmppMessageReceiptManager, SIGNAL(messageDelivered(QString,QString)), msgWrapper, SLOT(messageDelivered(QString,QString)) );

    /* multi user chats */
    xmppMucManager = new  QXmppMucManager();

    /* discovery manager */
    xmppDiscoveryManager = new QXmppDiscoveryManager();

    listModelChats = new ChatsListModel( this );

    flShowOffline = false;

    listModelRoster = new RosterListModel( this );

    m_bareJidLastMessage = "";
    m_resourceLastMessage = "";
    m_stateConnect = Disconnect;
    m_status = Offline;
    m_statusText = "";
    m_flTyping = false;
    m_myjid = "";
    m_password = "";
    m_host = "";
    m_port= 0;
    m_resource = "";
    m_chatJid = "";
    m_smilesmodel = 0;
    m_reconnectOnError = false;
    m_keepAlive = 60;

    flVCardRequest = "";
    qmlVCard = new QMLVCard();

    this->initXmppClient();

    rosterManager = 0;

    flSetPresenceWithoutAck = false;
}


MyXmppClient::~MyXmppClient()
{
    /*if( xmppClient != NULL ) {
        delete xmppClient;
    }*/

    if( cacheIM != NULL) {
        delete cacheIM;
    }

    if( msgWrapper != NULL) {
        delete msgWrapper;
    }

    /*if( xmppMessageReceiptManager != NULL) {
        delete xmppMessageReceiptManager;
    }*/
}



void MyXmppClient::initXmppClient()
{
    /* init home directory */
    /*bool res = */cacheIM->createHomeDir();

    QString p = cacheIM->getMeegIMHomePath() + "/roster.xml";
    rosterXml->createRosterXML( p );

    xmppClient->versionManager().setClientName("MeegIM");
    xmppClient->versionManager().setClientVersion( MyXmppClient::myVersion );

    /* add extension */
    xmppClient->addExtension( xmppMessageReceiptManager );

    xmppClient->addExtension( xmppMucManager );

    //xmppClient->addExtension( xmppDiscoveryManager );
}



void MyXmppClient::clientStateChanged(QXmppClient::State state)
{
    if( state == QXmppClient::ConnectingState )
    {
        m_stateConnect = Connecting;
    }
    else if( state == QXmppClient::ConnectedState )
    {
        m_stateConnect = Connected;

        if( !rosterManager ) //добавленное условие исключает повторный вызов connect
        {
            rosterManager = &xmppClient->rosterManager();
            //FIXME: добавил к коннеткам флаг Qt::UniqueConnection в виду того, что это состояние
            //может вызваться дважды. Последить за поведением клиента
            qDebug() << Q_FUNC_INFO << " QObject::connect( rosterManager, SIGNAL(........).....)";
            QObject::connect( rosterManager, SIGNAL(presenceChanged(QString,QString)), this, SLOT(initPresence(const QString, const QString)), Qt::UniqueConnection );
            QObject::connect( rosterManager, SIGNAL(rosterReceived()), this, SLOT(initRoster()), Qt::UniqueConnection );
            QObject::connect( rosterManager, SIGNAL(subscriptionReceived(QString)), this, SLOT(subscriptionReceivedSlot(QString)), Qt::UniqueConnection );
            QObject::connect( rosterManager, SIGNAL(itemAdded(QString)), this, SLOT(itemAdded(QString)), Qt::UniqueConnection );
            QObject::connect( rosterManager, SIGNAL(itemRemoved(QString)), this, SLOT(itemRemoved(QString)), Qt::UniqueConnection );
            QObject::connect( rosterManager, SIGNAL(itemChanged(QString)), this, SLOT(itemChanged(QString)), Qt::UniqueConnection );
        }

        /* проверка статуса для первоочередной установки,
         * пока не принялся сигнал, непосредственно извещающий о статусе
         */
        QXmppPresence pr = xmppClient->clientPresence();
        this->presenceReceived( pr );
    }
    else if( state == QXmppClient::DisconnectedState )
    {
        m_stateConnect = Disconnect;
        this->setMyPresence( Offline, m_statusText );
    }
    emit connectingChanged();
}



void MyXmppClient::connectToXmppServer() //Q_INVOKABLE
{
    //xmppConfig = mimOpt->getDefaultAccount();
    msgWrapper->setMyJid( m_myjid );

    QXmppConfiguration xmppConfig;

    xmppConfig.setJid( m_myjid );
    xmppConfig.setPassword( m_password );
    xmppConfig.setKeepAliveInterval( m_keepAlive );

    /*******************/
    //xmppConfig.setSaslAuthMechanism( "PLAIN" );
    xmppConfig.setAutoReconnectionEnabled( m_reconnectOnError );

    if( m_resource.isEmpty() || m_resource.isNull() ) {
        xmppConfig.setResource( "MeegIM" );
    } else {
        xmppConfig.setResource( m_resource );
    }

    if( !m_host.isEmpty() ) { xmppConfig.setHost( m_host ); }
    if( m_port != 0 ) { xmppConfig.setPort( m_port ); }

    xmppClient->connectToServer( xmppConfig );

    //TODO: здесь восстанавливть последний статус
}


void MyXmppClient::disconnectFromXmppServer() //Q_INVOKABLE
{
    xmppClient->disconnectFromServer();
}



/* it initialises the list of contacts - roster */
void MyXmppClient::initRoster()
{
    qDebug() << "MyXmppClient::initRoster() has been called";
    if( ! rosterManager->isRosterReceived() ) {
        qDebug() << "MyXmppClient::initRoster(): roster has not received yet";
        return;
    }

    if( !vCardManager )
    {
        vCardManager = &xmppClient->vCardManager();
        QObject::connect( vCardManager, SIGNAL(vCardReceived(const QXmppVCardIq &)),
                          this, SLOT(initVCard(const QXmppVCardIq &)),
                          Qt::UniqueConnection  );
    }


    QStringList listBareJids = rosterManager->getRosterBareJids();
    for( int j=0; j < listBareJids.length(); j++ )
    {
        QString bareJid = listBareJids.at(j);

        cacheIM->addCacheJid( bareJid );

        QXmppRosterIq::Item itemRoster = rosterManager->getRosterEntry( bareJid );
        QString name = itemRoster.name();
        QList<QString> listOfGroup = itemRoster.groups().toList();
        QString group = "";
        if( listOfGroup.length() > 0 ) {
            group = listOfGroup.at(0);
        }

        /* если в кеше есть аватарка и vCard, получим их */
        QString avatarPath = cacheIM->getAvatarCache( bareJid );
        vCardData vCdata = cacheIM->getVCard( bareJid );

        if ( avatarPath.isEmpty() && vCdata.isEmpty() )
        {
            /* кеш пустой, запрос vCard для текущего bareJid */
            vCardManager->requestVCard( bareJid );
            //qDebug() << "* request vCard for " << bareJid;
        }
        else
        {
            if( !avatarPath.isEmpty() ) {
                avatarPath = QString("file://") + avatarPath;
            }
            QString nickName = vCdata.nickName;
            if( (!nickName.isEmpty()) && (!nickName.isNull()) && (itemRoster.name().isEmpty()) ) {
                name =  nickName;
            }
        }

        RosterItemModel *itemModel = new RosterItemModel( );
        itemModel->setGroup( group );
        itemModel->setPicStatus( this->getPicPresence( QXmppPresence::Unavailable ) );
        itemModel->setContactName( name );
        itemModel->setJid( bareJid );
        itemModel->setAvatar( avatarPath );
        itemModel->setUnreadMsg( 0 );

        listRoster[ bareJid ] = itemModel;
    }

    this->setQMLListRoster();

    emit rosterChanged();

}



void MyXmppClient::initPresence(const QString& bareJid, const QString& resource)
{
    if( !listRoster.contains( bareJid ) ) {
        return;
    }

    QXmppPresence xmppPresence = rosterManager->getPresence( bareJid, resource );
    QXmppPresence::Type statusJid = xmppPresence.type();

    QStringList _listResources = this->getResourcesByJid( bareJid );
    //qDebug() << "*** initPresence():" << bareJid << " => " << resource << "_listResources:["<<_listResources<<"]";
    if( (_listResources.count() > 0) && (!_listResources.contains(resource)) )
    {
        /* делать выход ничего не меняя ?? */
        qDebug() << bareJid << "/" << resource << " ****************[" <<_listResources<<"]" ;
        if( statusJid == QXmppPresence::Unavailable ) {
            return;
        }
    }

    RosterItemModel *item = listRoster[ bareJid ];

    item->setResource( resource );

    QString picStatus = this->getPicPresence( xmppPresence );
    item->setPicStatus( picStatus );

    QString txtStatus = this->getTextStatus( xmppPresence.statusText(), xmppPresence );
    item->setTextStatus( txtStatus );
    qDebug() << "initPresence():" << bareJid << " => " << resource << " => " << txtStatus ;

    /* ищем контакт в ростере */
    int indxItem = -1;
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid, indxItem );

    if( flShowOffline )
    {
        /* --- показать offline контакты --- */
        if( itemExists != 0 ) {
            /* обновим информацию */
            itemExists->copy( item );
            QString picStatusPrev = itemExists->picStatus();
            if( picStatusPrev != picStatus ) //FIXME: пока таким экзотическим методом выясняем смнился ли статус собеседника по сравнению с предыдущим
            {
                //qDebug() << "1>>> presenceJidChanged:" << bareJid << "|"<<txtStatus<<"|"<<picStatus;
                emit presenceJidChanged( bareJid, txtStatus, picStatus );
            }
        }
    }
    else
    {
        /* --- скрыть оффлайн контакты --- */
        if( (itemExists==0) && (statusJid != QXmppPresence::Unavailable ) )
        {
            /* собеседник вошел в сеть: добавляем контакт в ростер */
            listModelRoster->append( item );
            //qDebug() << "2>>> presenceJidChanged:" << bareJid << "|"<<txtStatus<<"|"<<picStatus;
            emit presenceJidChanged( bareJid, txtStatus, picStatus );
        }
        else if( (itemExists!=0) && (statusJid != QXmppPresence::Unavailable ) )
        {
            /* собеседник сменил статус: обновить данные на ростере */
            itemExists->copy( item );
            QString picStatusPrev = itemExists->picStatus();
            if( picStatusPrev != picStatus ) //FIXME: пока таким экзотическим методом выясняем смнился ли статус собеседника по сравнению с предыдущим
            {
                //qDebug() << "3>>> presenceJidChanged:" << bareJid << "|"<<txtStatus<<"|"<<picStatus;
                emit presenceJidChanged( bareJid, txtStatus, picStatus );
            }
        }
        else if( (itemExists!=0) && (statusJid == QXmppPresence::Unavailable ) )
        {
            /* собеседник вышел из сети: удаляем контакт из ростера */
            if( indxItem >= 0 ) {
                listModelRoster->takeRow( indxItem );
                //qDebug() << "4>>> presenceJidChanged:" << bareJid << "|"<<txtStatus<<"|"<<picStatus;
                emit presenceJidChanged( bareJid, txtStatus, picStatus );
            }
        }
    }

    /* смотри, есть ли контакт в открытых чатах */
    RosterItemModel* item_chat = reinterpret_cast<RosterItemModel*>( listModelChats->find( bareJid ) );
    if( item_chat )
    {
        item_chat->setResource( resource );
        item_chat->setPicStatus( picStatus );
        item_chat->setTextStatus( txtStatus );
    }

}


void MyXmppClient::setQMLListRoster()
{
    listModelRoster->takeRows(0, listModelRoster->count() );
    if( flShowOffline )
    {
        /* show offline jids */
        QMap< QString, RosterItemModel*>::iterator itr;
        for ( itr = listRoster.begin(); itr != listRoster.end(); itr++ )
        {
            RosterItemModel* itemData = itr.value();
            listModelRoster->append( itemData );
        }
    }
    else if( m_stateConnect == Connected )
    {
        /* hide offline jids */
        QMap< QString, RosterItemModel*>::iterator itr;
        for ( itr = listRoster.begin(); itr != listRoster.end(); itr++ )
        {
            //QString jid = itr.key();
            RosterItemModel* itemData = itr.value();
            QXmppPresence presence( QXmppPresence::Unavailable );
            if( itemData->picStatus() != this->getPicPresence( presence ) ) { //пока так :(
                listModelRoster->append( itemData );
            }
        }
    }

    //qDebug() << "###!### m_status:" << m_status;
    if( m_stateConnect == Connected )
    {
        /* заполнение ростера из xml файла (muc) */
        QList<RosterItemModel *> listItems = rosterXml->getListMuc();
        QList<RosterItemModel *>::Iterator itr_xml;
        for ( itr_xml = listItems.begin(); itr_xml != listItems.end(); itr_xml++ )
        {
            QXmppPresence presence( QXmppPresence::Available );
            RosterItemModel* itemData = *itr_xml;
            itemData->setPicStatus( this->getPicPresence( presence ) );
            listModelRoster->append( itemData );
        }
    }
}


QString MyXmppClient::getPicPresence( const QXmppPresence &presence ) const
{
    QString picPresenceName = "qrc:/qml/images/presence-unknown.png";

    QXmppPresence::Type status = presence.type();
    if( status != QXmppPresence::Available )
    {
        picPresenceName = "qrc:/qml/images/presence-offline.png";
    }
    else
    {
        QXmppPresence::AvailableStatusType availableStatus = presence.availableStatusType();
        if( (availableStatus == QXmppPresence::Online) || (availableStatus == QXmppPresence::Chat) ) {
            picPresenceName = "qrc:/qml/images/presence-online.png";
        } else if ( (availableStatus == QXmppPresence::Away) || (availableStatus == QXmppPresence::XA) ) {
            picPresenceName = "qrc:/qml/images/presence-away.png";
        } else if ( availableStatus == QXmppPresence::DND ) {
            picPresenceName = "qrc:/qml/images/presence-busy.png";
        }
    }

    return picPresenceName;
}



QString MyXmppClient::getTextStatus(const QString &textStatus, const QXmppPresence &presence ) const
{
    if( (!textStatus.isEmpty()) && (!textStatus.isNull()) ) {
        return textStatus;
    }

    QXmppPresence::Type status = presence.type();

    QString txtStat = "";
    if( status == QXmppPresence::Unavailable )
    {
        txtStat = "Offline";
    }
    else
    {
        QXmppPresence::AvailableStatusType availableStatus = presence.availableStatusType();

        if( availableStatus == QXmppPresence::Online ) {
            txtStat = "Online";
        } else if ( availableStatus == QXmppPresence::Chat ) {
            txtStat = "Chatty";
        } else if ( availableStatus == QXmppPresence::Away ) {
            txtStat = "Away";
        } else if ( availableStatus == QXmppPresence::XA ) {
            txtStat = "Extended away";
        } else if ( availableStatus == QXmppPresence::DND ) {
            txtStat = "Do not disturb";
        }
    }

    return txtStat;
}



/* SLOT: it will be called when the vCardReceived signal will be received */
void MyXmppClient::initVCard(const QXmppVCardIq &vCard)
{
    QString bareJid = vCard.from();
    //qDebug() << "## initVCard: " << bareJid;

    RosterItemModel *item = (RosterItemModel*)listModelRoster->find( bareJid );

    vCardData dataVCard;

    if( /*item != 0*/true )
    {
        /* set nickname */
        QXmppRosterIq::Item itemRoster = rosterManager->getRosterEntry( bareJid );
        QString nickName = vCard.nickName();
        //if( (!nickName.isEmpty()) && (!nickName.isNull()) && (item!=0) ) {
        if( (!nickName.isEmpty()) && (!nickName.isNull()) && (itemRoster.name().isEmpty()) && (item!=0) ) {
            //qDebug() << "*****> setContactName( nickName ): " << nickName;
            item->setContactName( nickName );
        }

        /* avatar */
        bool isAvatarCreated = true;
        QString avatarFile = cacheIM->getAvatarCache( bareJid );
        if( avatarFile.isEmpty() || (flVCardRequest != "") ) {
            isAvatarCreated =  cacheIM->setAvatarCache( bareJid, vCard.photo() );
            avatarFile = cacheIM->getAvatarCache( bareJid );
        }
        if( isAvatarCreated ) {
            avatarFile = QString("file://") + avatarFile;
            if( item != 0 ) {
                item->setAvatar( avatarFile );
            }
        }

        dataVCard.nickName = nickName;
        dataVCard.firstName = vCard.firstName();
        dataVCard.fullName = vCard.fullName();;
        dataVCard.middleName = vCard.middleName();
        dataVCard.lastName = vCard.lastName();
        dataVCard.url = vCard.url();
        dataVCard.eMail = vCard.email();

        if( flVCardRequest == bareJid ) {
            //qDebug() << "MyXmppClient::initVCard(): [" << flVCardRequest << "] is ready";
            qmlVCard->setPhoto( avatarFile );
            qmlVCard->setNickName( vCard.nickName() );
            qmlVCard->setMiddleName( vCard.middleName() );
            qmlVCard->setLastName( vCard.lastName() );
            qmlVCard->setFullName( vCard.fullName() );
            qmlVCard->setName( vCard.firstName() );
            qmlVCard->setBirthday( vCard.birthday().toString("dd.MM.yyyy") );
            qmlVCard->setEMail( vCard.email() );
            qmlVCard->setUrl( vCard.url() );
            qmlVCard->setJid( bareJid );
            flVCardRequest = "";
            emit vCardChanged();
        }

        //qDebug() << dataVCard.nickName;
        //qDebug() << dataVCard.firstName;
        //qDebug() << dataVCard.fullName;
        //qDebug() << dataVCard.middleName;
        //qDebug() << dataVCard.lastName;
        //qDebug() << dataVCard.url;
        //qDebug() << dataVCard.eMail;

        cacheIM->setVCard( bareJid, dataVCard );
        //qDebug() << "----------------------------";
    }

}


void MyXmppClient::setStatusText( const QString &__statusText )
{
    if( __statusText != m_statusText )
    {
        m_statusText=__statusText;

        QXmppPresence myPresence = xmppClient->clientPresence();
        myPresence.setStatusText( __statusText );
        xmppClient->setClientPresence( myPresence );

        //mimOpt->setStatusText( __statusText );

        emit statusTextChanged();
    }
}


void MyXmppClient::setStatus( StatusXmpp __status)
{
    if( __status != m_status )
    {
        QXmppPresence myPresence = xmppClient->clientPresence();

        if( __status == Online ) {
            myPresence.setType( QXmppPresence::Available );
            myPresence.setAvailableStatusType( QXmppPresence::Online );
        } else if( __status ==  Chat ) {
            myPresence.setType( QXmppPresence::Available );
            myPresence.setAvailableStatusType( QXmppPresence::Chat );
        } else if ( __status == Away ) {
            myPresence.setType( QXmppPresence::Available );
            myPresence.setAvailableStatusType( QXmppPresence::Away );
        } else if ( __status == XA ) {
            myPresence.setType( QXmppPresence::Available );
            myPresence.setAvailableStatusType( QXmppPresence::XA );
        } else if( __status == DND ) {
            myPresence.setType( QXmppPresence::Available );
            myPresence.setAvailableStatusType( QXmppPresence::DND );
        } else if( __status == Offline ) {
            myPresence.setType( QXmppPresence::Unavailable );
            //m_status = __status;
            //emit statusChanged();

            /* ОСТАНОВКА ПЕРЕКОННЕКТА */ //FIXME: QXmppReconnectionManager was removed
            //QXmppReconnectionManager *rm = xmppClient->reconnectionManager();
            //if( rm ) { rm->cancelReconnection(); }
        }

        xmppClient->setClientPresence( myPresence );
        this->presenceReceived( myPresence ); //NOTICE: извещаем клиент о смене статуса мгновенно
    }
}



void MyXmppClient::setMyPresence( StatusXmpp status, QString textStatus ) //Q_INVOKABLE
{
    qDebug() << Q_FUNC_INFO;
    if( textStatus != m_statusText ) {
        m_statusText =textStatus;
        emit statusTextChanged();
    }

    QXmppPresence myPresence;

    if( status == Online )
    {
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            this->connectToXmppServer();
        }
        myPresence.setType( QXmppPresence::Available );
        myPresence.setStatusText( textStatus );
        myPresence.setAvailableStatusType( QXmppPresence::Online );
    }
    else if( status == Chat )
    {
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            this->connectToXmppServer();
        }
        myPresence.setType( QXmppPresence::Available );
        myPresence.setAvailableStatusType( QXmppPresence::Chat );
        myPresence.setStatusText( textStatus );
    }
    else if( status == Away )
    {
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            this->connectToXmppServer();
        }
        myPresence.setType( QXmppPresence::Available );
        myPresence.setAvailableStatusType( QXmppPresence::Away );
        myPresence.setStatusText( textStatus );
    }
    else if( status == XA )
    {
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            this->connectToXmppServer();
        }
        myPresence.setType( QXmppPresence::Available );
        myPresence.setAvailableStatusType( QXmppPresence::XA );
        myPresence.setStatusText( textStatus );
    }
    else if( status == DND )
    {
        if( xmppClient->state()  == QXmppClient::DisconnectedState ) {
            this->connectToXmppServer();
        }
        myPresence.setType( QXmppPresence::Available );
        myPresence.setAvailableStatusType( QXmppPresence::DND );
        myPresence.setStatusText( textStatus );
    }
    else if( status == Offline )
    {
        if( (xmppClient->state()  == QXmppClient::ConnectedState)  || (xmppClient->state()  == QXmppClient::ConnectingState) )
        {
            xmppClient->disconnectFromServer();
        }
        myPresence.setType( QXmppPresence::Unavailable );

        /* все контакты offline */
        QMap< QString, RosterItemModel*>::iterator itr;
        for ( itr = listRoster.begin(); itr != listRoster.end(); itr++ )
        {
            QXmppPresence presnc( QXmppPresence::Unavailable );
            RosterItemModel* itemData = itr.value();
            QString pic = this->getPicPresence( presnc );
            itemData->setPicStatus( pic );
            QString st = this->getTextStatus( "", presnc );
            itemData->setTextStatus( st );
        }
        /* извещаем об оффлайн состоянии */
        //m_status = status;
        //this->setQMLListRoster();
        //emit statusChanged();

        /* ОСТАНОВКА ПЕРЕКОННЕКТА */ //FIXME: QXmppReconnectionManager was removed
        //QXmppReconnectionManager *rm = xmppClient->reconnectionManager();
        //if( rm ) { rm->cancelReconnection(); }
    }

    xmppClient->setClientPresence( myPresence  );
    this->presenceReceived( myPresence ); //NOTICE: извещаем клиент о смене статуса мгновенно

    if( status == Offline ) {
        this->setQMLListRoster();
    }
}



/* it sends information about typing : typing is started */
void MyXmppClient::typingStart(QString bareJid, QString resource) //Q_INVOKABLE
{
    //qDebug() << bareJid << " " << "start typing...";
    QXmppMessage xmppMsg;

    QString jid_to = bareJid;
    if( resource == "" ) {
        jid_to += "/resource";
    } else {
        jid_to += "/" + resource;
    }
    xmppMsg.setTo( jid_to );

    QString jid_from = m_myjid + "/" + xmppClient->configuration().resource();
    xmppMsg.setFrom( jid_from );

    xmppMsg.setReceiptRequested( false );

    QDateTime currTime = QDateTime::currentDateTime();
    xmppMsg.setStamp( currTime );

    xmppMsg.setState( QXmppMessage::Composing );

    xmppClient->sendPacket( xmppMsg );
}


/* it sends information about typing : typing is stoped */
void MyXmppClient::typingStop(QString bareJid, QString resource) //Q_INVOKABLE
{
    //qDebug() << bareJid << " " << "stop typing...";
    QXmppMessage xmppMsg;

    QString jid_to = bareJid;
    if( resource == "" ) {
        jid_to += "/resource";
    } else {
        jid_to += "/" + resource;
    }
    xmppMsg.setTo( jid_to );

    QString jid_from = m_myjid + "/" + xmppClient->configuration().resource();
    xmppMsg.setFrom( jid_from );

    xmppMsg.setReceiptRequested( false );

    QDateTime currTime = QDateTime::currentDateTime();
    xmppMsg.setStamp( currTime );

    xmppMsg.setState( QXmppMessage::Paused );

    xmppClient->sendPacket( xmppMsg );
}



void MyXmppClient::openChat( QString bareJid ) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::openChat(): new chat was opened:" << bareJid;
    /* поиск jid в ростере */
    RosterItemModel *itemRoster =  reinterpret_cast<RosterItemModel*>( listModelRoster->find( bareJid ) );

    /* поиск соответствующего jid в открытых чатах */
    RosterItemModel* item = reinterpret_cast<RosterItemModel*>( listModelChats->find( bareJid ) );
    bool isMuc = false;
    if( (item == 0) && (itemRoster != 0) ) // исключаем вторичное добавление
    {
        listModelChats->append(itemRoster);
        isMuc = (itemRoster->itemType() == 1);
    }
    else if( (item == 0) && (itemRoster == 0) ) //сообщение от собеседника которого нет в ростере
    {
        //qDebug() << "XXX:" << bareJid;
        QXmppPresence presence( QXmppPresence::Unavailable );
        RosterItemModel *newItem = new RosterItemModel( );
        newItem->setGroup( "" );
        newItem->setPicStatus( this->getPicPresence( presence ) );
        newItem->setContactName( "" );
        newItem->setJid( bareJid );
        newItem->setAvatar( "" );
        newItem->setUnreadMsg( 0 );

        listModelChats->append( newItem );
    }

    emit chatOpened( bareJid, isMuc );
    emit openChatsChanged( bareJid, isMuc );

    msgWrapper->initChat( bareJid );
    this->resetUnreadMessages( bareJid );
}


void MyXmppClient::closeChat( QString bareJid ) //Q_INVOKABLE
{
    int row = -1;
    this->resetUnreadMessages( bareJid );
    RosterItemModel *item = reinterpret_cast<RosterItemModel*>( listModelChats->find( bareJid, row ) );
    if( (item != NULL) && (row >= 0) )
    {
        /*bool res = */listModelChats->takeRow( row );

        if( item->itemType() == 1 )
        {
            /* chat is closed, therefor logout from the chat */
        }
        bool isMuc = item->itemType() == ROSTER_ITEM_MUC;

        emit openChatsChanged( bareJid, isMuc );
        emit chatClosed( bareJid, isMuc );
        //qDebug() << "MyXmppClient::closeChat("<<bareJid<<"): row:"<<row << " result:"<<res << " listModelChats.count():" <<listModelChats->count();
    }
    msgWrapper->removeListOfChat( bareJid );
}


void MyXmppClient::incUnreadMessage(QString bareJid) //Q_INVOKABLE
{
    RosterItemModel *item = (RosterItemModel*)listModelRoster->find( bareJid );

    if( item != 0 )
    {
        int cnt = item->unreadMsg();
        item->setUnreadMsg( ++cnt );
    }
}


void MyXmppClient::resetUnreadMessages(QString bareJid) //Q_INVOKABLE
{
    RosterItemModel *item = (RosterItemModel*)listModelRoster->find( bareJid );
    if( item != 0 ) {
        item->setUnreadMsg( 0 );
    }

    RosterItemModel *itemChat = (RosterItemModel*)listModelChats->find( bareJid );
    if( itemChat != 0 ) {
        itemChat->setUnreadMsg( 0 );
    }
}


void MyXmppClient::itemAdded(const QString &bareJid )
{
    qDebug() << "MyXmppClient::itemAdded(): " << bareJid;
    QStringList resourcesList = rosterManager->getResources( bareJid );

    /* добавляем в ростер */
    QXmppPresence presence( QXmppPresence::Unavailable );
    RosterItemModel *itemModel = new RosterItemModel( );
    itemModel->setGroup("");
    itemModel->setPicStatus( this->getPicPresence( presence ) );
    itemModel->setContactName("");
    itemModel->setJid( bareJid );
    itemModel->setAvatar("");
    itemModel->setUnreadMsg( 0 );
    listRoster[ bareJid ] = itemModel;

    /* запрашиваем присутствие */
    for( int L = 0; L<resourcesList.length(); L++ )
    {
        QString resource = resourcesList.at(L);
        this->initPresence( bareJid, resource );
    }
}


void MyXmppClient::itemChanged(const QString &bareJid )
{
    qDebug() << "MyXmppClient::itemChanged(): " << bareJid;

    QXmppRosterIq::Item rosterEntry = rosterManager->getRosterEntry( bareJid );
    QString name = rosterEntry.name();
    //qDebug() << "rosterEntry.name()=" << name;

    RosterItemModel *item = (RosterItemModel*)listModelRoster->find( bareJid );
    if( item ) {
        item->setContactName( name );
    }

}


void MyXmppClient::itemRemoved(const QString &bareJid )
{
    qDebug() << "MyXmppClient::itemRemoved(): " << bareJid;

    if( listRoster.contains( bareJid ) )
    {
        listRoster.remove( bareJid );
    }

    int indxItem = -1;
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid, indxItem );
    if( itemExists )
    {
        /* удаляем контакт из ростера */
        if( indxItem >= 0 ) {
            listModelRoster->takeRow( indxItem );
        }
    }
}


void MyXmppClient::subscriptionReceivedSlot(const QString &bareJid )
{
    emit this->subscriptionReceived( bareJid );
}


void MyXmppClient::requestVCard(QString bareJid) //Q_INVOKABLE
{
    if (vCardManager && (flVCardRequest == "") )
    {
        vCardManager->requestVCard( bareJid );
        flVCardRequest = bareJid;
    }
}


void MyXmppClient::messageReceivedSlot( const QXmppMessage &xmppMsg )
{
    QString bareJid_from = MyXmppClient::getBareJidByJid( xmppMsg.from() );
    QString bareJid_to = MyXmppClient::getBareJidByJid( xmppMsg.to() );
    qDebug() << Q_FUNC_INFO << " typeMsg: " << xmppMsg.type();

    if( xmppMsg.state() == QXmppMessage::Active )
    {
        qDebug() << "Msg state is QXmppMessage::Active";
        if( !( xmppMsg.body().isEmpty() || xmppMsg.body().isNull()) )
        {
            msgWrapper->textMessage(xmppMsg);
            QString jid = xmppMsg.from();
            if( jid.indexOf('/') >= 0 ) {
                QStringList sl =  jid.split('/');
                m_bareJidLastMessage = sl[0];
                if( sl.count() > 1 ) {
                    m_resourceLastMessage = sl[1];
                }
            } else {
                m_bareJidLastMessage = xmppMsg.from();
            }

            this->incUnreadMessage( bareJid_from );
            emit this->messageReceived( bareJid_from, bareJid_to );
        }
    }
    else if( xmppMsg.state() == QXmppMessage::Inactive )
    {
        qDebug() << "Msg state is QXmppMessage::Inactive";
    }
    else if( xmppMsg.state() == QXmppMessage::Gone )
    {
        qDebug() << "Msg state is QXmppMessage::Gone";
    }
    else if( xmppMsg.state() == QXmppMessage::Composing )
    {
        m_flTyping = true;
        emit typingChanged( bareJid_from, true);
    }
    else if( xmppMsg.state() == QXmppMessage::Paused )
    {
        m_flTyping = false;
        emit typingChanged( bareJid_from, false);
    }
    else
    {
        QString mucInvitationJid = xmppMsg.mucInvitationJid(); //в случае если это приглшение, строка будет не пустой

        if( xmppMsg.isAttentionRequested() )
        {
            //qDebug() << "ZZZ: attentionRequest !!! from:" <<xmppMsg.from();
            msgWrapper->attention( bareJid_from, false );
            this->incUnreadMessage( bareJid_from );
            emit this->messageReceived( bareJid_from, bareJid_to );
        }
        else if( mucInvitationJid.length() > 1 )
        {
            //qDebug() << "ZZZ: mucInvitationJid:" << mucInvitationJid;
        }
        else if( !( xmppMsg.body().isEmpty() || xmppMsg.body().isNull()) )
        {
            msgWrapper->textMessage(xmppMsg);

            QString jid = xmppMsg.from();
            if( jid.indexOf('/') >= 0 ) {
                QStringList sl =  jid.split('/');
                m_bareJidLastMessage = sl[0];
                if( sl.count() > 1 ) {
                    m_resourceLastMessage = sl[1];
                }
            } else {
                m_bareJidLastMessage = xmppMsg.from();
            }

            this->incUnreadMessage( bareJid_from );
            emit this->messageReceived( bareJid_from, bareJid_to );
        }
        qDebug() << "MessageWrapper::messageReceived(): xmppMsg.state():" << xmppMsg.state();
    }
}

void MyXmppClient::setShowOffline(const bool value)
{
    if( flShowOffline != value )
    {
        flShowOffline = value;

        this->setQMLListRoster();

        emit showOfflineChanged();
        emit rosterChanged();
    }
}


QString MyXmppClient::getPicPresenceByJid(QString bareJid)
{
    QString ret = "";
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid );
    if( itemExists ) {
        ret = itemExists->picStatus();
    }
    return ret;
}


QString MyXmppClient::getStatusTextByJid(QString bareJid)
{
    QString ret = "";
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid );
    if( itemExists ) {
        ret = itemExists->textStatus();
    }
    return ret;
}

QString MyXmppClient::getAvatarByJid(QString bareJid)
{
    QString ret = "";
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid );
    if( itemExists ) {
        ret = itemExists->picAvatar();
    }
    return ret;
}


bool MyXmppClient::sendMyMessage(QString bareJid, QString resource, QString msgBody) //Q_INVOKABLE
{
    if( msgBody == "" ) { return false; }

    QXmppMessage xmppMsg; //???
    QString jid_from = bareJid;
    if( resource == "" ) {
        jid_from += "/resource";
    } else {
        jid_from += "/" + resource;
    }
    xmppMsg.setTo( jid_from );
    QString jid_to = m_myjid + "/" + xmppClient->configuration().resource();
    xmppMsg.setFrom( jid_to );

    //qDebug() << "SEND MSG: from:[" << jid_to << "] to:[" << jid_from << "] body:[" << msgBody << "]";

    /*QTextDocument doc;
    doc.setHtml( msgBody );
    xmppMsg.setBody( doc.toPlainText() );*/

    xmppMsg.setBody( msgBody );

    xmppMsg.setReceiptRequested( true );  /* request DLR */

    xmppMsg.setState( QXmppMessage::Active );

    //qDebug() << "MyXmppClient::sendMyMessage: "<<jid_from<<" "<<jid_to<<" "<<msgBody;
    xmppClient->sendPacket( xmppMsg );

    this->messageReceivedSlot( xmppMsg );

    return true;
}


QStringList MyXmppClient::getResourcesByJid(QString bareJid)
{
    return rosterManager->getResources(bareJid);
}


void MyXmppClient::presenceReceived( const QXmppPresence & presence )
{
    QString jid = presence.from();
    QString bareJid = jid;
    QString resource = "";
    if( jid.indexOf('/') >= 0 ) {
        bareJid = jid.split('/')[0];
        resource = jid.split('/')[1];
    }
    //this->initPresence( bareJid, resource );
    QString myResource = xmppClient->configuration().resource();

    //qDebug() << "### MyXmppClient::presenceReceived():" << bareJid << "|" << resource << "|" << myResource << "|" << presence.from() << "|" << presence.type()<< "|" << presence.availableStatusType();
    if( (((presence.from()).indexOf( m_myjid ) >= 0) && (resource == myResource)) || ((bareJid == "") && (resource == "")) )
    {
        QXmppPresence::Type __type = presence.type();
        if( __type == QXmppPresence::Unavailable )
        {
            m_status = Offline;
        }
        else
        {
            QXmppPresence::AvailableStatusType __status = presence.availableStatusType();
            if( __status == QXmppPresence::Online ) {
                m_status = Online;
            } else if( __status ==  QXmppPresence::Chat ) {
                m_status = Chat;
            } else if ( __status == QXmppPresence::Away ) {
                m_status = Away;
            } else if ( __status == QXmppPresence::XA ) {
                m_status = XA;
            } else if( __status == QXmppPresence::DND ) {
                m_status = DND;
            }
        }

        emit statusChanged();

        //qDebug() << "^^^ MyXmppClient::presenceReceived(): for " << bareJid << " status is " << presence.type();
    }
}


void MyXmppClient::error(QXmppClient::Error e)
{
    QString errString;
    if( e == QXmppClient::SocketError ) {
        errString = "Socket error";
    } else if( e == QXmppClient::KeepAliveError ) {
        errString = "Keep alive error";
    } else if( e == QXmppClient::XmppStreamError ) {
        errString = "Xmpp stream error";
    }

    if( !errString.isNull() )
    {
        /* проверяем и устанавливаем текущее состояние клиента */
        QXmppPresence pr = xmppClient->clientPresence();
        this->presenceReceived( pr );

        /* ОСТАНОВКА ПЕРЕКОННЕКТА */
        //если нужен переконнект при ошибке то не выполнять две строки ниже
        if( !m_reconnectOnError ) {
            QXmppPresence presence( QXmppPresence::Unavailable );
            xmppClient->setClientPresence( presence );
        }

        /* извещаем об ошибке */
        emit errorHappened( errString );
    }
}


/*--- add/remove contact ---*/
void MyXmppClient::addContact( QString bareJid, QString nick, QString group, bool sendSubscribe )
{
    //qDebug() << "MyXmppClient::addContact(" << bareJid << ", " << nick << ", " << group << ", " << sendSubscribe << ")";
    if( rosterManager )
    {
        QSet<QString> gr;
        QString n;
        if( !(group.isEmpty() || group.isNull()) )  { gr.insert( group ); }
        if( !(nick.isEmpty() || nick.isNull()) )  { n = nick; }
        rosterManager->addItem(bareJid, n, gr );

        if( sendSubscribe ) {
            rosterManager->subscribe( bareJid );
        }
    }
}

void MyXmppClient::removeContact( QString bareJid ) //Q_INVOKABLE
{
    int row = 0;
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( bareJid, row );
    if( itemExists )
    {
        int type = itemExists->itemType();
        if ( type == 1 )
        {
            listModelRoster->takeRow( row );
            rosterXml->removeMuc( bareJid );
        }
        else
        {
            if( rosterManager ) {
                rosterManager->removeItem( bareJid );
            }
        }
    }
}


void MyXmppClient::renameContact(QString bareJid, QString name) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::renameContact(" << bareJid << ", " << name << ")" ;
    if( rosterManager ) {
        rosterManager->renameItem( bareJid, name );
    }
}


bool MyXmppClient::subscribe(const QString bareJid) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::subscribe(" << bareJid << ")" ;
    bool res = false;
    if( rosterManager && (!bareJid.isEmpty()) && (!bareJid.isNull()) ) {
        res = rosterManager->subscribe( bareJid );
    }
    return res;
}


bool MyXmppClient::unsubscribe(const QString bareJid) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::unsubscribe(" << bareJid << ")" ;
    bool res = false;
    if( rosterManager && (!bareJid.isEmpty()) && (!bareJid.isNull()) ) {
        res = rosterManager->unsubscribe( bareJid );
    }
    return res;
}


bool MyXmppClient::acceptSubscribtion(const QString bareJid) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::acceptSubscribtion(" << bareJid << ")" ;
    bool res = false;
    if( rosterManager && (!bareJid.isEmpty()) && (!bareJid.isNull()) ) {
        res = rosterManager->acceptSubscription( bareJid );
    }
    return res;
}


bool MyXmppClient::rejectSubscribtion(const QString bareJid) //Q_INVOKABLE
{
    //qDebug() << "MyXmppClient::rejectSubscribtion(" << bareJid << ")" ;
    bool res = false;
    if( rosterManager && (!bareJid.isEmpty()) && (!bareJid.isNull()) ) {
        res = rosterManager->refuseSubscription( bareJid );
    }
    return res;
}


void MyXmppClient::addMucRoster( QString mucJid, QString mucSubject, bool addToRoster   )
{
    /* ищем такой jid в ростере */
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( mucJid );

    QXmppPresence presence( QXmppPresence::Available );

    /* если в ростере не нашли, добавляем */
    RosterItemModel *newItem = itemExists;
    if( itemExists == 0 ) {
        newItem = new RosterItemModel( );
        newItem->setGroup( "Multi-user chats" );
        newItem->setPicStatus( this->getPicPresence( presence ) );
        newItem->setContactName( "" );
        newItem->setJid( mucJid );
        newItem->setAvatar( "" );
        newItem->setUnreadMsg( 0 );
        newItem->setTextStatus( mucSubject );
        newItem->setItemType( ROSTER_ITEM_MUC ); //1 - muc

        listModelRoster->append( newItem ); //add to roster
    }

    /* ищем среди открытых */
    RosterItemModel *openExists = (RosterItemModel*)listModelChats->find( mucJid );
    /* если не находим, открываем */
    if( openExists == 0 ) {
        listModelChats->append( newItem ); //open multi-user chat
        emit chatOpened( mucJid, true );
        emit openChatsChanged( mucJid, true );
    }

    if( addToRoster ) {
        rosterXml->addMuc( newItem );
    }
}


void MyXmppClient::setMucSubject( QString mucJid, QString mucSubject  )
{
    RosterItemModel *itemExists = (RosterItemModel*)listModelRoster->find( mucJid );

    if( itemExists ) {
        itemExists->setTextStatus( mucSubject );
    }
}

void MyXmppClient::attentionSend( QString bareJid, QString resource )
{
    qDebug() << Q_FUNC_INFO;
    QXmppMessage xmppMsg;

    QString jid_to = bareJid;
    if( resource == "" ) {
        jid_to += "/resource";
    } else {
        jid_to += "/" + resource;
    }
    xmppMsg.setTo( jid_to );

    QString jid_from = m_myjid + "/" + xmppClient->configuration().resource();
    xmppMsg.setFrom( jid_from );

    xmppMsg.setReceiptRequested( false );

    xmppMsg.setState( QXmppMessage::None );
    xmppMsg.setType( QXmppMessage::Headline );
    xmppMsg.setAttentionRequested( true );

    xmppClient->sendPacket( xmppMsg );

    msgWrapper->attention( bareJid, true );
}

/*void MyXmppClient::invitationMucReceived(const QString &roomJid, const QString &inviter, const QString &reason)
{
    qDebug() << Q_FUNC_INFO << roomJid <<" " << inviter << " " << reason;
}*/


